%{
#include <stdio.h>
#include <stdlib.h>

void output_section(const char* section);
void save_cur_section();
void save_all_section();

void save_comment(const char* strComment,int type);

static char szBuf[1024];
static int bufIdx = 0;
static int bKeySet = 0;

void append_keys();
void append_values();

static char szInputFile[1024] = "";
static char szOutputFile[1024]= "";
static char szSwitchMacro[256] = "_MGINCORE_RES";
static char szExportMain[256] = "__mg_get_mgetc";
static int buildInMiniGUI = 1;
#define TAB "    "

#ifdef WIN32
static inline int isatty(int fd) {
	return 1;
}
#endif

%}

%x DEFINE
%x COMMENT

%%

"#{{"		{ BEGIN DEFINE;  bufIdx = 0; }
<DEFINE>\n	{ BEGIN 0; szBuf[bufIdx] = 0; save_comment(szBuf,1); bufIdx = 0; yylineno ++;}
<DEFINE>.	{ szBuf[bufIdx++] = yytext[0]; }
"#}}"[^\n]*\n		{ save_comment("endif",1);bufIdx = 0; yylineno++; }

"#"			{ BEGIN COMMENT; bufIdx = 0; }
<COMMENT>\n	{ BEGIN 0; szBuf[bufIdx] = 0;  save_comment(szBuf, 0); bufIdx = 0;  yylineno ++; }
<COMMENT>.	{ szBuf[bufIdx++] = yytext[0]; }

\[[^\]]+\]\n	{ save_cur_section(); output_section(yytext); yylineno++; }

^[ \t]+\n	{ yylineno++; }
[ \t]+	;
=		{ szBuf[bufIdx] = 0;  append_keys(); bufIdx = 0; bKeySet = 1; }
\n		{ szBuf[bufIdx] = 0; if(bKeySet)append_values(); bufIdx = 0; bKeySet = 0;  yylineno++; }
.		{ szBuf[bufIdx++] = yytext[0]; }

%%

int yywrap(){
	return 1;
}

static void set_header()
{
	fprintf(yyout,"/*************************************************\n");
	fprintf(yyout,"* This is inside mode of system res configuation *\n");
	fprintf(yyout,"* It's generated by the mgcfg-trans, version 1.0 *\n");
	fprintf(yyout,"* author : dongjunjie in feynman                 *\n");
	fprintf(yyout,"* please donnot modify this file, if you want,   *\n");
	fprintf(yyout,"* please change your input file and regenerate   *\n");
    fprintf(yyout,"* this file                                      *\n");
	fprintf(yyout,"*************************************************/\n");

	fprintf(yyout,"#include <stdio.h>\n");
    if(buildInMiniGUI)
    {
	    fprintf(yyout,"#include \"common.h\"\n");
	    fprintf(yyout,"#include \"minigui.h\"\n");
    }
    else
    {
        fprintf(yyout, "#include <minigui/common.h>\n");
        fprintf(yyout, "#include <minigui/minigui.h>\n");
    }

	fprintf(yyout,"\n#ifdef %s\n\n", szSwitchMacro);

}

static void set_tail()
{
	fprintf(yyout,"\n");
	fprintf(yyout,"///////////////////////////////////////////////////\n");
	fprintf(yyout,"\n");
	fprintf(yyout,"static ETC_S _ETC = {\n");
	fprintf(yyout,TAB"0,\n");
	fprintf(yyout,TAB"sizeof(_etc_sections)/sizeof(ETCSECTION),\n");
	fprintf(yyout,TAB"_etc_sections\n");
	fprintf(yyout,"};\n");
	fprintf(yyout,"\n");

	fprintf(yyout,"GHANDLE %s (void)\n", szExportMain);
	fprintf(yyout,"{\n");
	fprintf(yyout,TAB"return (GHANDLE) &_ETC;\n");
	fprintf(yyout,"}\n\n");

	fprintf(yyout,"#endif /* %s */\n\n", szSwitchMacro);
}

static void process_args(int argc, const char* argv[]);
int main(int argc, const char* argv[])
{
    FILE *fin, *fout;
    process_args(argc, argv); 
    fin = fopen(szInputFile, "rt");
    fout = fopen(szOutputFile, "wt");
    if(!fin)
        fin = stdin;
    if(!fout)
        fout = stdout;
    yyset_in(fin);
    yyset_out(fout);

	set_header();
	yylex();
	save_cur_section();
	save_all_section();
	set_tail();
	return 0;
}


///////////////////////////////////////////
//list implement

struct list_node_t{
	void *data;
	struct list_node_t * next;
};

#define NEWEX(Type, n)	(Type*)calloc(n,sizeof(Type))
#define NEW(Type) NEWEX(Type,1)
struct list_node_t * new_node(void *data)
{
	struct list_node_t* ln = NEW(struct list_node_t);
	ln->data = data;
	ln->next = NULL;
	return ln;
}

struct list_node_t *append_node(struct list_node_t* cur, struct list_node_t* node)
{
	if(cur) cur->next = node;
	node->next = NULL;
	return node;
}

void free_nodes(struct list_node_t* head, void(*free_node)(void*))
{
	struct list_node_t *tmp;
	while(head){
		tmp = head;
		head = head->next;
		if(free_node)
			(*free_node)(tmp->data);
		free(tmp);
	}
}

/////////////////////////////////////////

struct list_node_t* print_comments(struct list_node_t* node,int lineno_begin, int lineno_end,int flag);

typedef struct _section_t {
	char* name;
    char* cname;
	int count;
	int lineno;
}section_t;
static void free_section(section_t* sec){
	if(sec){
		if(sec->name) free(sec->name);
        if(sec->cname) free(sec->cname);
		free(sec);
	}
}

static section_t* cur_section = NULL;
static struct list_node_t* section_nodes = NULL, *cur_section_node;

static inline char ToHex(int v)
{
    v = v&0xF;
    if(v >= 0 && v <= 9)
        return v + '0';
    else
        return v + 'A' - 10;
}

static char* translate_secname(const char* secname)
{
    char szName[1024];
    int  i;
    int  idx = 0;
    if(!secname)
        return NULL;
    for(i = 0; secname[i]; i ++)
    {
        if(secname[i] >='A' && secname[i] <='Z'
            || secname[i] >= 'a' && secname[i] <='z'
            || secname[i] == '_'
            || secname[i]>='0' && secname[i] <= '9')
        {
            szName[idx++] = secname[i];
        }
        else if(secname[i] == '.' || secname[i] == ':' || secname[i] == '-')
            szName[idx++] = '_';
        else
        {
            szName[idx++] = ToHex(0xF&secname[i]);
            szName[idx++] = ToHex(secname[i] >> 4);
        }
    }
    szName[idx] = 0;
    if(idx > 0)
        return strdup(szName);
    else
        return NULL;
}

void output_section(const char* section)
{
	if(section== NULL || strlen(section) == 0)
		return;
	
	section = strchr(section,'[');
	if(section == NULL)
		return;
	
	section ++;
	const char* str = strrchr(section, ']');
	
	int len;
	if(str == NULL) len = strlen(section);
	else
		len = str - section;

	char* secname = NEWEX(char,(len+1));
	strncpy(secname, section, len);
	secname[len] = 0;

	cur_section = NEW(section_t);
	cur_section->name = secname;
    cur_section->cname = translate_secname(secname);
	cur_section->count = 0;
	cur_section->lineno = yylineno;

	struct list_node_t* node = new_node(cur_section);
	cur_section_node = append_node(cur_section_node, node);
	
	if(section_nodes == NULL)
		section_nodes = cur_section_node;
		
}


typedef struct _string_info_t {
	char* str;
	int lineno;
}string_info_t;

void free_string_info_t(string_info_t* si)
{
	if(si){
		if(si->str) free(si->str);
		free(si);
	}
}

static struct list_node_t* _keys=NULL, *_values=NULL;
static struct list_node_t* _cur_keys=NULL, *_cur_values=NULL;

struct list_node_t* append_str(struct list_node_t* cur, const char* str)
{
	char* stext = NULL;
	string_info_t *si = NULL;
	if(str != NULL || strlen(str) != 0)
	{
		stext = strdup(str);
		si = NEW(string_info_t);
		si->str = stext;
		si->lineno = yylineno;
	}
	
	struct list_node_t* node = new_node(si);
	return append_node(cur, node);
}

void append_keys()
{
	_cur_keys = append_str(_cur_keys, szBuf);
	if(_keys == NULL) _keys = _cur_keys;
	cur_section->count ++;
}

void append_values()
{
	_cur_values = append_str(_cur_values, szBuf);
	if(_values == NULL) _values = _cur_values;
}



/////////////////////////////////////
static int last_comment_lineno = 0;
static struct list_node_t * last_comment_nodes = NULL;

void save_strings(struct list_node_t *head)
{
	while(head)
	{
		string_info_t* si = (string_info_t*) head->data;
		if(si){
			last_comment_nodes = print_comments(last_comment_nodes,last_comment_lineno,si->lineno,3);
			last_comment_lineno = si->lineno;
			fprintf(yyout,TAB"\"%s\"%s\n", si->str,head->next?",":"");
		}
		else
			fprintf(yyout,TAB"\"\"%s\n",head->next?",":"");
		head = head->next;
	}
	free_nodes(head,free);
}

void save_keys(const char* str)
{
	fprintf(yyout,"static char* _%s_keys[]={ \n",str);
	save_strings(_keys);
	_keys = _cur_keys = NULL;
	fprintf(yyout,"};\n");
}

void save_values(const char* str)
{
	fprintf(yyout,"static char* _%s_values[]={ \n", str);
	save_strings(_values);
	_values = _cur_values = NULL;
	fprintf(yyout,"};\n");
}


void save_cur_section()
{

	if(cur_section==NULL) return ;


	//printf comments
	last_comment_nodes = print_comments(last_comment_nodes, last_comment_lineno, cur_section->lineno,3);
	last_comment_lineno = cur_section->lineno;

	fprintf(yyout,"// Section: %s\n",cur_section->name);
	struct list_node_t* node = last_comment_nodes;
	save_keys(cur_section->cname);
	last_comment_nodes = node;
	last_comment_lineno = cur_section->lineno;
	save_values(cur_section->cname);
}


////////////////////
//comment
typedef struct _comment_info_t{
	char * str;
	int type;
	int lineno;
}comment_info_t;

void free_comment(comment_info_t* ci)
{
	if(ci){
		if(ci->str) 
			free(ci->str);
		free(ci);
	}
}

static struct list_node_t * _comments, *_cur_comment;

void save_comment(const char* strComment,int type)
{
	if(strComment == NULL && strlen(strComment) == 0)
		return;

	comment_info_t *ci = NEW(comment_info_t);

	ci->str = strdup(strComment);
	ci->type = type;
	ci->lineno = yylineno;
	
	_cur_comment = append_node(_cur_comment,new_node(ci));

	if(_comments == NULL)
		_comments = _cur_comment;
}

struct list_node_t* print_comments(struct list_node_t* node,int lineno_begin, int lineno_end,int flag)
{
	if(node == NULL) node = _comments;
	struct list_node_t *ln = node;
	comment_info_t *ci;

	while(ln){
		ci = (comment_info_t*)ln->data;
		if(ci && ci->lineno >= lineno_begin)
			break;
		ln = ln->next;
	}
	
	while(ln){
		ci = (comment_info_t*)ln->data;
		if(ci->lineno >= lineno_end) break;
		if((flag&2) && ci->type == 1)
			fprintf(yyout,"#%s\n",ci->str);
		else if(flag&1)
			fprintf(yyout,"//%s\n",ci->str);
		ln = ln->next;
	}
	return ln;
}

void save_all_section()
{
	section_t * sec;
	struct list_node_t * ln = section_nodes;
	struct list_node_t * old_comment_nodes = last_comment_nodes;
	int old_comment_lineno = last_comment_lineno;
	last_comment_nodes = NULL;
	last_comment_lineno = 0;

	fprintf(yyout,"static ETCSECTION _etc_sections [] = { \n");

	while(ln){
		sec = (section_t*)ln->data;
		if(sec){
			last_comment_nodes = print_comments( last_comment_nodes, last_comment_lineno, sec->lineno, 2);
			last_comment_lineno = sec->lineno;
			fprintf(yyout,TAB"{0, %d, \"%s\", _%s_keys,_%s_values }%s \n",
				sec->count, sec->name, sec->cname, sec->cname,
				ln->next?",":"");
		}
		ln = ln->next;
	}
	last_comment_nodes = print_comments( last_comment_nodes, last_comment_lineno, 0xFFFFFF, 2);
	fprintf(yyout,"};\n");


	last_comment_nodes = old_comment_nodes;
	last_comment_lineno = old_comment_lineno;

	print_comments(last_comment_nodes,last_comment_lineno,0xFFFFFF,3);

	free_nodes(section_nodes, (void(*)(void*))free_section);
	free_nodes(_comments, (void(*)(void*))free_comment);
}

////////////////////////////////////////////////////////////////////////////////////////
static void usage(const char* app)
{
    printf("Usage %s [options] -o <output-file> <input-file>\n", app);
    printf("\toptions: --switch-marco <switch-marco>\n\t--export-main <function>\n");
    printf("\t--build-out -B\t build the output file out of minigui\n");
    printf("\t-h -? for help\n");
}

static void process_args(int argc, const char* argv[])
{
    int i;
    if(argc <= 1)
    {
        usage(argv[0]);
        exit(0);
    }

    for(i = 1; i < argc; i ++)
    {
        if(argv[i][0] == '-') //options
        {
            const char* str = argv[i] + 1;
            if(argv[i][1] =='-')
                str ++;

            if(strcmp(str, "h") == 0
                || strcmp(str, "?") == 0
                || strcmp(str, "help") == 0)
            {
                usage(argv[0]);
                exit(0);
            }
            else if(strcmp(str,"switch-marco") == 0)
            {
                strcpy(szSwitchMacro, argv[i+1]);
                i ++;
            }
            else if(strcmp(str, "export-main") == 0)
            {
                strcpy(szExportMain, argv[i+1]);
                i ++;
            }
            else if(strcmp(str,"o") == 0 || strcmp(str, "output") == 0)
            {
                strcpy(szOutputFile, argv[i+1]);
                i ++;
            }
            else if(strcmp(str,"build-out") == 0 || strcmp(str, "B") == 0)
            {
                buildInMiniGUI = 0;
            }
            else
            {
                printf("unknown option:%s\n",str);
                exit(1);
            }
        }
        else
        {
            strcpy(szInputFile, argv[i]);
        }
    }

}

